use std::{
    f32::consts::{FRAC_PI_2, FRAC_PI_3, TAU},
    f64::consts::FRAC_2_PI,
};

use bevy::{
    animation::{AnimationTarget, AnimationTargetId, animated_field},
    diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
    ecs::storage::ResourceData,
    math::NormedVectorSpace,
    prelude::*,
};
use rand::{Rng, RngCore, SeedableRng, rngs::ThreadRng};
use rand_chacha::ChaCha8Rng;

mod map;

fn main() {
    App::new()
        .add_plugins((
            DefaultPlugins,
            FrameTimeDiagnosticsPlugin::default(),
            LogDiagnosticsPlugin::default(),
        ))
        .add_systems(Startup, setup)
        .add_systems(Startup, cube_animation)
        .add_systems(Update, move_player)
        .add_systems(Update, auto_move_to)
        .add_systems(Update, auto_random_move)
        .run();
}

#[derive(Resource)]
struct RandomSource(ChaCha8Rng);

#[derive(Component)]
struct MoveTo(Vec3);

#[derive(Component)]
struct Flyer;

fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
    asset_server: Res<AssetServer>,
) {
    // light
    commands.spawn((
        DirectionalLight {
            shadows_enabled: true,
            ..default()
        },
        Transform::from_xyz(10.0, 10.0, 0.0).looking_at(Vec3::ZERO, Vec3::Y),
    ));

    // camera
    commands.spawn((
        Camera3d::default(),
        Transform::from_xyz(8.5, 30.0, 8.5).looking_at(Vec3::new(8.5, 0.0, 8.5), Vec3::Y),
    ));

    commands.insert_resource(RandomSource(ChaCha8Rng::from_os_rng()));

    // 棋盘
    // for x in 0..17 {
    //     for z in 0..17 {
    //         commands.spawn((
    //             Mesh3d(meshes.add(Cuboid::new(0.95, 0.95, 0.95))),
    //             MeshMaterial3d(materials.add(Color::srgb_u8(124, 144, 255))),
    //             Transform::from_xyz(x as f32, 0.0, z as f32)
    //                 .mul_transform(Transform::from_xyz(0.0, 0.0, 0.0)),
    //         ));
    //     }
    // }
    commands.spawn((
        Mesh3d(meshes.add(Plane3d::default().mesh().size(17.0, 17.0))),
        MeshMaterial3d(materials.add(StandardMaterial {
            perceptual_roughness: 0.4,
            base_color_texture: Some(asset_server.load("textures/plane.png")),
            ..default()
        })),
        Transform::from_xyz(8., 0.0, 8.),
    ));
}

fn cube_animation(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
    mut animation_graphs: ResMut<Assets<AnimationGraph>>,
    mut animation_clips: ResMut<Assets<AnimationClip>>,
) {
    let animation_target_name = Name::new("fly");
    let animation_target_id = AnimationTargetId::from_name(&animation_target_name);
    let mut animation_clip = AnimationClip::default();

    let translation_curve = EasingCurve::new(
        vec3(-6., 2., 0.),
        vec3(6., 2., 0.),
        EaseFunction::CubicInOut,
    )
    .reparametrize_linear(interval(0., 3.0).unwrap())
    .unwrap()
    .ping_pong()
    .unwrap();

    let rotation_curve = EasingCurve::new(
        Quat::IDENTITY,
        Quat::from_rotation_y(FRAC_PI_3),
        EaseFunction::ElasticInOut,
    )
    .reparametrize_linear(interval(0.0, 4.0).unwrap())
    .unwrap();

    animation_clip.add_curve_to_target(
        animation_target_id,
        AnimatableCurve::new(animated_field!(Transform::translation), translation_curve),
    );
    animation_clip.add_curve_to_target(
        animation_target_id,
        AnimatableCurve::new(animated_field!(Transform::rotation), rotation_curve),
    );

    let animation_clip_handle = animation_clips.add(animation_clip);
    let (animation_graph, animation_node_index) = AnimationGraph::from_clip(animation_clip_handle);
    let animation_graph_handle = animation_graphs.add(animation_graph);

    let mut animation_player = AnimationPlayer::default();
    animation_player.play(animation_node_index);

    // 飞机
    for i in 0..16 {
        commands.spawn((
            Flyer,
            Mesh3d(meshes.add(Cuboid::new(0.7, 0.7, 0.7))),
            MeshMaterial3d(materials.add(Color::srgb(1.0, 0.0, 0.0))),
            Transform::from_xyz(0.0, 2.0, 0.0),
        ));
    }
    commands.spawn((
        Flyer,
        Mesh3d(meshes.add(Cuboid::new(0.7, 0.7, 0.7))),
        MeshMaterial3d(materials.add(Color::srgb(1.0, 0.0, 0.0))),
        Transform::from_xyz(0.0, 2.0, 0.0),
        animation_target_name,
        animation_player,
        AnimationGraphHandle(animation_graph_handle),
    ));
}

fn move_player(
    mut commands: Commands,
    keyboard_input: Res<ButtonInput<KeyCode>>,
    fly_entities: Query<Entity, (With<Flyer>, Without<MoveTo>)>,
    mut rng: ResMut<RandomSource>,
) {
    if keyboard_input.pressed(KeyCode::Space) {
        let fly_entity_number = fly_entities.iter().len();
        // println!("fly_entities number: {fly_entity_number}");
        for fly_entity in fly_entities {
            let target_position = Vec3::new(
                rng.0.random_range(0..17) as f32,
                1.0,
                rng.0.random_range(0..17) as f32,
            );
            commands.entity(fly_entity).insert(MoveTo(target_position));
        }
    }
}

fn auto_move_to(
    mut commands: Commands,
    entities: Query<(Entity, &MoveTo, &mut Transform), With<MoveTo>>,
    time: Res<Time>,
) {
    for (entity, moveTo, mut transform) in entities {
        match Dir3::new(moveTo.0 - transform.translation) {
            Ok(dir) => {
                let time_interval = time.delta_secs();
                let path_lenth = (moveTo.0 - transform.translation).norm();
                let path_lenth = path_lenth.min(time_interval * 11.0);
                let path = path_lenth * dir;
                transform.translation += path;

                // 旋转
                let rotate_y = Quat::from_rotation_y(FRAC_PI_2);
                let rotate_ax = rotate_y * dir;
                transform.rotate_axis(rotate_ax, time_interval * 720.0 * TAU / 360.0);
            }
            Err(_) => {
                commands.entity(entity).remove::<MoveTo>();
                ()
            }
        }
    }
}

fn auto_random_move(
    mut commands: Commands,
    entities: Query<Entity, (Without<MoveTo>, With<Flyer>)>,
    mut rng: ResMut<RandomSource>,
) {
    for entity in entities {
        let target_position = Vec3::new(
            rng.0.random_range(0..17) as f32,
            1.0,
            rng.0.random_range(0..17) as f32,
        );
        commands.entity(entity).insert(MoveTo(target_position));
    }
}
